package com.lab.network.proxy;

import com.alibaba.fastjson.JSONObject;
import com.cuitrip.apiservice.BusinessHelper;
import com.cuitrip.util.text.EmojiFilterUtils;
import com.lab.logtrack.AppLog;
import com.lab.network.model.CtApiRequest;
import com.lab.network.model.CtApiResponse;
import com.lab.network.model.CtRemoteBusiness;
import com.lab.network.model.IProxyCallback;
import com.lab.network.model.ParseType;
import com.lab.network.model.ProxyResult;
import com.lab.network.model.ResultCallback;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

/**
 * Created by ak on 15/11/16.
 */
public class ApiProxy extends Proxy {

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    public static final String PROXY_NAME = "ApiProxy";
    public static final int APIPROXY_TYPE_RESPONSE = 0x10000001;
    private OkHttpClient mOkHttpClient;
    private boolean shouldResponseOnUiThread = true;
    private boolean asyncExecute = true;

    public ApiProxy(IProxyCallback proxyCallback) {
        super(proxyCallback);
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(65,TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(65, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(65,TimeUnit.SECONDS);
    }

    public void setShouldResponseOnUiThread(boolean shouldResponseOnUiThread) {
        this.shouldResponseOnUiThread = shouldResponseOnUiThread;
    }

    public void setAsyncExecute(boolean asyncExecute) {
        this.asyncExecute = asyncExecute;
    }

    public void post(CtRemoteBusiness ctRemoteBusiness, ParseType parseType) {
        if (asyncExecute) {
            postAsync(ctRemoteBusiness, parseType);
        } else {
            postSync(ctRemoteBusiness, parseType);
        }
    }

    public Call postAsync(final CtRemoteBusiness ctRemoteBusiness, ParseType parseType) {
        Request request = buildRequest(ctRemoteBusiness);
        if (request == null) {
            return null;
        }
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new ResultCallback(parseType) {
            @Override
            public void onResponse(CtApiResponse ctApiResponse) {
                ApiProxyResult proxyResult = new ApiProxyResult();
                proxyResult.setType(APIPROXY_TYPE_RESPONSE);
                proxyResult.setData(ctApiResponse);
                proxyResult.setmActionName(ctRemoteBusiness.request.getApiName());
                ctApiResponse.setApiName(ctRemoteBusiness.request.getApiName());
                setProxyResult(proxyResult, shouldResponseOnUiThread);
                AppLog.d("http method:" + ctRemoteBusiness.request.getApiName() + " , response:" + ctApiResponse.toString());
            }
        });
        return call;
    }

    public ApiProxyResult postSync(CtRemoteBusiness ctRemoteBusiness, ParseType parseType) {
        Request request = buildRequest(ctRemoteBusiness);
        ApiProxyResult proxyResult = new ApiProxyResult();
        if (request == null) {
            return proxyResult;
        }
        Call call = mOkHttpClient.newCall(request);
        try {
            Response response = call.execute();
            CtApiResponse ctApiResponse = new ResultCallback(parseType) {
                @Override
                public void onResponse(CtApiResponse ctApiResponse) {

                }
            }.handleResponse(response);
            proxyResult.setType(APIPROXY_TYPE_RESPONSE);
            proxyResult.setData(ctApiResponse);
            proxyResult.setmActionName(ctRemoteBusiness.request.getApiName());
            ctApiResponse.setApiName(ctRemoteBusiness.request.getApiName());
            setProxyResult(proxyResult, false);
            AppLog.d("http method:" + ctRemoteBusiness.request.getApiName() + " , response:" + ctApiResponse.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return proxyResult;
    }

    private Request buildRequest(CtRemoteBusiness ctRemoteBusiness) {
        if (ctRemoteBusiness == null) {
            return null;
        }
        CtApiRequest ctApiRequest = ctRemoteBusiness.request;
        HashMap<String, Object> dataParams = ctApiRequest.getDataParams();
        LinkedHashMap<String, Object> temp = new LinkedHashMap<String, Object>();
        temp.putAll(dataParams);
        Iterator iterator = dataParams.entrySet().iterator();
        final String apiName = ctApiRequest.getApiName();
        JSONObject jsonObject = new JSONObject();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry<String, Object>) iterator.next();
            Object value = entry.getValue();
            if (value instanceof String){
                value = EmojiFilterUtils.filterEmoji((String)value);
            }
            jsonObject.put(entry.getKey(), value);
        }
        RequestBody requestBody = RequestBody.create(JSON, jsonObject.toJSONString());
        Request request = new Request.Builder()
                .url(BusinessHelper.getApiUrl(ctRemoteBusiness.request.getApiName()))
                .post(requestBody)
                .build();
        AppLog.d("http method:" + apiName + " , request:" + jsonObject.toString());
        return request;
    }


    public Call postWithProgress(CtRemoteBusiness ctRemoteBusiness, ParseType parseType) {
        if (ctRemoteBusiness == null) {
            return null;
        }

        CtApiRequest ctApiRequest = ctRemoteBusiness.request;
        HashMap<String, Object> dataParams = ctApiRequest.getDataParams();
        LinkedHashMap<String, Object> temp = new LinkedHashMap<String, Object>();
        temp.putAll(dataParams);
        Iterator iterator = dataParams.entrySet().iterator();
        final String apiName = ctApiRequest.getApiName();
        JSONObject jsonObject = new JSONObject();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry<String, Object>) iterator.next();
            jsonObject.put(entry.getKey(), entry.getValue());
        }
        RequestBody requestBody = RequestBody.create(JSON, jsonObject.toJSONString());
        Request request = new Request.Builder()
                .url(BusinessHelper.getApiUrl(ctRemoteBusiness.request.getApiName()))
                .post(requestBody)
                .build();


        final ProgressListener progressListener = new ProgressListener() {
            @Override public void update(long bytesRead, long contentLength, boolean done) {
            }
        };

        mOkHttpClient.networkInterceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                return originalResponse.newBuilder()
                        .body(new ProgressResponseBody(originalResponse.body(), progressListener))
                        .build();
            }
        });

        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new ResultCallback(parseType) {
            @Override
            public void onResponse(CtApiResponse ctApiResponse) {
                ApiProxyResult proxyResult = new ApiProxyResult();
                proxyResult.setType(APIPROXY_TYPE_RESPONSE);
                proxyResult.setData(ctApiResponse);
                proxyResult.setmActionName(apiName);
                ctApiResponse.setApiName(apiName);
                setProxyResult(proxyResult);
            }
        });
        return call;
    }




    private static class ProgressResponseBody extends ResponseBody {

        private final ResponseBody responseBody;
        private final ProgressListener progressListener;
        private BufferedSource bufferedSource;

        public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }

        @Override public MediaType contentType() {
            return responseBody.contentType();
        }

        @Override public long contentLength() throws IOException {
            return responseBody.contentLength();
        }

        @Override public BufferedSource source() throws IOException {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;
                @Override public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    // read() returns the number of bytes read, or -1 if this source is exhausted.
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
                    return bytesRead;
                }
            };
        }
    }

    interface ProgressListener {
        void update(long bytesRead, long contentLength, boolean done);
    }

    public class ApiProxyResult extends ProxyResult {

        public ApiProxyResult() {
            setProxy(ApiProxy.class);
            setProxyName(ApiProxy.PROXY_NAME);
        }
    }


}
